package gatperdut.chato.core.interpreter.actions;

import gatperdut.chato.constants.CSConstantsCommandResult.CommandExecutionResult;
import gatperdut.chato.core.characters.BaseController;
import gatperdut.chato.core.interpreter.actions.generic.Action;
import gatperdut.chato.core.interpreter.grammar.comminfo.SetsInfo;
import gatperdut.chato.core.item.DrinkController;
import gatperdut.chato.core.item.FoodController;
import gatperdut.chato.core.item.ItemController;

public class SetsAction extends Action {

    public SetsAction( SetsInfo info, BaseController entityController ) {
        super( info, entityController );
    }

    @Override
    public CommandExecutionResult perform() {
        checkForRunningCraft();

        ItemController set1 = findInRoomOrInventory( getInfo().getSet1(), getInfo().isGround1() );
        if ( set1 == null ) {
            getEntityController().tx( "You cannot find the first stack." );
            return CommandExecutionResult.OK;
        }

        if ( !isAnAppropriateStack( set1 ) ) {
            return CommandExecutionResult.OK;
        }

        switch ( getInfo().getSetsOperation() ) {
        case MERGE:
            ItemController set2 = findInRoomOrInventory( getInfo().getSet2(), getInfo().isGround2() );
            if ( set2 == null ) {
                getEntityController().tx( "You cannot find the second stack." );
                return CommandExecutionResult.OK;
            }
            if ( !isAnAppropriateStack( set2 ) ) {
                return CommandExecutionResult.OK;
            }

            if ( set1 == set2 ) {
                getEntityController().tx( "Cannot merge " + set1.getShortDesc() + " with itself." );
                return CommandExecutionResult.OK;
            }
            if ( set1.getItem().getTemplate() != set2.getItem().getTemplate() ) {
                getEntityController().tx( "Can only merge items of same type." );
                return CommandExecutionResult.OK;
            }
            if ( set1.isStackable() ) {
                mergeStackable( set1, set2 );
            }
            else if ( set1.isEatable() || set1.isDrinkable() ) {
                FoodController set1FC = (FoodController) set1.getAmountController();
                FoodController set2FC = (FoodController) set2.getAmountController();
                if ( set1.isDrinkable() ) {
                    DrinkController set1DC = (DrinkController) set1.getAmountController();
                    DrinkController set2DC = (DrinkController) set2.getAmountController();
                    if ( set1DC.hasNoPortionLeft() && set2DC.hasNoPortionLeft() ) {
                        mergeEmptyDrinkable( set1, set2 );
                        return CommandExecutionResult.OK;
                    }
                    if ( set1DC.hasNoPortionLeft() || set2DC.hasNoPortionLeft() ) {
                        getEntityController().tx( "Empty liquid containers cannot be merged in stacks." );
                        return CommandExecutionResult.OK;
                    }
                    if ( !set1DC.containsThisFluid( set2DC.getFluidContent() ) ) {
                        getEntityController().tx( "Can only merge liquid containers with the same type of liquid." );
                        return CommandExecutionResult.OK;
                    }
                }
                int portion = set1FC.getPortion() + set2FC.getPortion();
                if ( portion > set1FC.getMaxPortion() ) {
                    set1FC.setPortion( portion - set1FC.getMaxPortion() );
                    set2.junk();
                }
                else {
                    if ( set1.isDrinkable() && ( set1FC.getCurrent() == 1 || set2FC.getCurrent() == 1 ) ) {
                        getEntityController().tx( "Maybe you should try pouring one into the other?" );
                        return CommandExecutionResult.OK;
                    }
                    set1FC.setPortion( portion );
                    set1FC.decrease( 1 );
                    if ( set1.isDrinkable() ) {
                        ItemController newContainer = getEntityController().getChatoServer().getItemHandler().copyItemController( set1 );
                        newContainer.getAmountController().setCurrent( 1 );
                        ( (DrinkController) newContainer.getAmountController() ).setPortion( 0 );
                        set2.junk();
                        getEntityController().handleSpawnedItem( set1.getContainerController(), newContainer );
                    }
                    else {
                        set2.junk();
                    }
                }

                set1FC.increase( set2FC.getCurrent() );
            }
            getEntityController().tx( "You merge both stacks." );
            return CommandExecutionResult.OK;
        case SPLIT:
            if ( getInfo().getAmount() <= 0 ) {
                getEntityController().tx( "That number does not make any sense, does it?" );
                return CommandExecutionResult.OK;
            }
            if ( getInfo().getAmount() >= set1.getAmountController().getCurrent() ) {
                getEntityController().tx( "There's only " + set1.getAmountController().getCurrent() + " of them." );
                return CommandExecutionResult.OK;
            }
            ItemController newStack = getEntityController().getChatoServer().getItemHandler().copyItemController( set1 );
            if ( set1.isStackable() ) {
                splitStackable( set1, newStack );
            }
            else if ( set1.isEatable() || set1.isDrinkable() ) {
                splitFood( set1, newStack );
            }
            getEntityController().handleSpawnedItem( set1.getContainerController(), newStack );

            return CommandExecutionResult.OK;
        default:
            throw new IllegalArgumentException( "Sets operation with unknown operation type." );
        }
    }

    private void splitFood( ItemController set1, ItemController newStack ) {
        FoodController set1FC = (FoodController) set1.getAmountController();
        int newStackAmount = set1FC.getCurrent() - getInfo().getAmount();
        getEntityController().tx( "You split " + set1.getShortDesc() + " into two stacks of " + getInfo().getAmount() + " and " + newStackAmount + "." );
        set1FC.setCurrent( newStackAmount );
        if ( !set1FC.hasNoPortionLeft() ) {
            ( (FoodController) newStack.getAmountController() ).setToMaxPortion();
        }
        newStack.getAmountController().decrease( getInfo().getAmount() );
        set1FC.setCurrent( getInfo().getAmount() );
    }

    private void splitStackable( ItemController set1, ItemController newStack ) {
        int newStackAmount = set1.getAmountController().getCurrent() - getInfo().getAmount();
        newStack.getAmountController().setCurrent( newStackAmount );
        getEntityController().tx( "You split " + set1.getShortDesc() + " into two stacks of " + getInfo().getAmount() + " and " + newStackAmount + "." );
        set1.getAmountController().setCurrent( getInfo().getAmount() );
    }

    private void mergeStackable( ItemController set1, ItemController set2 ) {
        if ( set1.getAmountController().canAffordIncrease( set2.getAmountController().getCurrent() ) ) {
            set1.getAmountController().increase( set2.getAmountController().getCurrent() );
            set2.junk();
        }
        else {
            int taken = set1.getAmountController().getMax() - set1.getAmountController().getCurrent();
            set1.getAmountController().setToMax();
            set2.getAmountController().decrease( taken );
        }
    }

    private void mergeEmptyDrinkable( ItemController set1, ItemController set2 ) {
        ( (DrinkController) set1.getAmountController() ).increase( ( (DrinkController) set2.getAmountController() ).getCurrent() );
        set2.junk();
        getEntityController().tx( "You make a new stack of empty containers." );
    }

    private boolean isAnAppropriateStack( ItemController ic ) {
        if ( !ic.isStackable() && !ic.isEatable() && !ic.isDrinkable() ) {
            getEntityController().tx( ic.getShortDesc() + " is neither stackable nor a food item." );
            return false;
        }
        else {
            return true;
        }
    }

    @Override
    public boolean interruptsCrafting() {
        return false;
    }

    @Override
    public SetsInfo getInfo() {
        return (SetsInfo) super.getInfo();
    }
}
